package nothing;

import java.math.BigDecimal;
import java.sql.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import nothing.dao.CookDao;
import nothing.model.Cooked;
import nothing.model.Diary;
import nothing.model.Food;
import nothing.model.FoodNutrition;
import nothing.model.Ingredient;
import nothing.model.Qty;
import nothing.model.Recipe;
import nothing.model.RecipeNutrition;
import nothing.model.RecipeType;
import nothing.model.RecipeTime;
import org.appfuse.model.User;

import nothing.Util;
import nothing.model.MeasurementRatio;
import nothing.model.Purchase;
import nothing.model.RecipeStep;
import nothing.model.RecipeCost;

public class CookManager {
	static Logger log = Logger.getLogger(CookManager.class);
	private CookDao cookDao = null;

	private static long DAY_IN_SEC = 86400000;
	private static boolean MULTI_RECIPE_PER_DAY = true;

	public CookManager(CookDao cookDao) {
		this.setCookDao(cookDao);
	}

	public void setCookDao(CookDao cookDao) {
		this.cookDao = cookDao;
	}

	public void storeCooked(Cooked cooked) {
		cookDao.getCookedDao().save(cooked);
	}

	public List<RecipeType> getAllRecipeType() {
		return cookDao.getRecipeTypeDao().getAll();
	}

	public List<RecipeCost> getRecipeByPrice() {

		List<Ingredient> list = cookDao.getIngredientDao().getAll();

		// get price result and consolidate qtys
		Hashtable<String, RecipeCost> recipes = new Hashtable<String, RecipeCost>();
		for (Iterator<Ingredient> iterator = list.listIterator(); iterator
				.hasNext();) {
			Ingredient ingredient = iterator.next();
			
			List<Food> unKnownFoodCost = new ArrayList<Food>();
			List<MeasurementRatio> unKnownRatio = new ArrayList<MeasurementRatio>();
			
			List<Purchase> purchaseList = cookDao.getPurchaseDao().findByFoodName(
					ingredient.getFood().getName());

			RecipeCost recipeCost = recipes.remove(ingredient.getRecipe().getName());
			BigDecimal calc = new BigDecimal("0");
			SimpleDateFormat sdf = new SimpleDateFormat("dd-mm-yy");
			java.util.Date date = null;
			try {
				date = sdf.parse("21-07-71");
			} catch (ParseException e) {
				log.error("Bad date", e);
			}

			if (purchaseList.isEmpty()) {
				unKnownFoodCost.add(ingredient.getFood());
			} else {
				if (purchaseList.get(0).getWhen().after(date)){
					date = purchaseList.get(0).getWhen();
				}				
				calc = calculateCost(purchaseList.get(0), ingredient, unKnownRatio);
			}	

			log.debug(ingredient+" Costs "+calc);

			if (recipeCost != null) {
				if (recipeCost.getDate().after(date)){
					date = recipeCost.getDate();
				}	
				recipeCost.setCost(recipeCost.getCost().add(calc));
			} else {
				recipeCost = new RecipeCost(ingredient.getRecipe()
						,calc
						,date
						,unKnownFoodCost
						,unKnownRatio);  
			}
			recipeCost.setDate(date);
			
			recipes.put(ingredient.getRecipe().getName(), recipeCost);
		}
		RecipeCost[] consolidatedList = new RecipeCost[recipes.size()];  
		recipes.values().toArray(consolidatedList);

		// sort by cost
		Arrays.sort(consolidatedList, new CostComparator());
		List<RecipeCost> recipeCostList = Arrays.asList(consolidatedList);

		return recipeCostList;
	}

	private BigDecimal calculateCost(Purchase purchase, Ingredient ingredient,
			List<MeasurementRatio> unKnownRatio) {
		BigDecimal qty = new BigDecimal(0);
		BigDecimal purchaseCost = purchase.getCost().divide(purchase.getQty());
		if (purchase.getMeasurement().equals(ingredient.getMeasurement())) {
			qty = ingredient.getQty();
		} else {
			List<MeasurementRatio> measurementRatio = this.cookDao
					.getMeasurementRatioDao().findByMeasurementName(
							purchase.getMeasurement().getName(),
							ingredient.getMeasurement().getName());
			if (measurementRatio == null || measurementRatio.size() == 0) {
				MeasurementRatio mr = new MeasurementRatio();
				mr.setManyMeasurement(ingredient.getMeasurement());
				mr.setOneMeasurement(purchase.getMeasurement());
				unKnownRatio.add(mr);
			} else {
				MeasurementRatio ratio = measurementRatio.get(0);
				if (ratio.getManyMeasurement()
						.equals(purchase.getMeasurement())) {
					qty = purchase.getQty().multiply(ratio.getQty());
				} else {
					qty = purchase.getQty().divide(ratio.getQty());
				}

			}
		}
		return qty.multiply(purchaseCost);
	}

	public List<RecipeNutrition> getRecipeByNutrition() {

		List<Ingredient> list = cookDao.getIngredientDao().getAll();

		// get nutrition result and consolidate qtys
		Hashtable<String, RecipeNutrition> recipes = new Hashtable<String, RecipeNutrition>();
		for (Iterator<Ingredient> iterator = list.listIterator(); iterator
				.hasNext();) {
			Ingredient ingredient = iterator.next();

			List<Food> unKnownFoodNutrition = new ArrayList<Food>();
			List<MeasurementRatio> unKnownRatio = new ArrayList<MeasurementRatio>();

			List<FoodNutrition> nutritionList = cookDao.getFoodNutritionDao()
					.findByFoodName(ingredient.getFood().getName());
			
			if (nutritionList == null) {
				unKnownFoodNutrition.add(ingredient.getFood());
			}
			RecipeNutrition recipeNutrition = (RecipeNutrition) recipes.remove(ingredient
					.getRecipe().getName());
			if (recipeNutrition == null){
				recipeNutrition = new RecipeNutrition(
						ingredient.getRecipe(), 
						new Hashtable<FoodNutrition,Qty>(),
						new ArrayList<FoodNutrition>(), 
						new ArrayList<MeasurementRatio>()); 
			}
			
			Hashtable<FoodNutrition,Qty> nutritions = recipeNutrition.getNutritionList();
			for (FoodNutrition foodNutrition:nutritions.keySet()){ 
				Qty qty = nutritions.remove(foodNutrition);				
				if (qty == null){
					qty = new Qty(new BigDecimal(0),foodNutrition.getMeasurement());				
				}
				// need to work out what the result is
				BigDecimal calc = calculateNutrition(foodNutrition, ingredient, unKnownRatio);
				qty.setQty(qty.getQty().add(calc));
			
				// add food Nutrition. What about QTY, what about recipe steps
				nutritions.put(foodNutrition, qty);
			}
			
		}	
		RecipeNutrition[] consolidatedList = new RecipeNutrition[recipes.values().size()]; 
		consolidatedList = (RecipeNutrition[]) recipes.values().toArray(consolidatedList);
		// sort by cost
		Arrays.sort(consolidatedList, new RecipeNutritionComparator());
		
		return Arrays.asList(consolidatedList);
		
	}

	private BigDecimal calculateNutrition(FoodNutrition foodNutrition,
			Ingredient ingredient, List<MeasurementRatio> unKnownRatio) {

		BigDecimal nutritionQty = foodNutrition.getQty();
		BigDecimal qty = new BigDecimal(0);
		if (foodNutrition.getMeasurement().equals(ingredient.getMeasurement())) {
			if (foodNutrition.getPer().compareTo(ingredient.getQty()) < 0) {
				qty = ingredient.getQty().divide(foodNutrition.getPer());
			} else if (foodNutrition.getPer().compareTo(ingredient.getQty()) == 0) {
				qty = foodNutrition.getPer();
			} else {
				qty = ingredient.getQty().multiply(foodNutrition.getPer());
			}
		} else {
			List<MeasurementRatio> measurementRatio = this.cookDao
					.getMeasurementRatioDao().findByMeasurementName(
							foodNutrition.getMeasurement().getName(),
							ingredient.getMeasurement().getName());
			if (measurementRatio == null || measurementRatio.size() == 0) {
				MeasurementRatio mr = new MeasurementRatio();
				mr.setManyMeasurement(ingredient.getMeasurement());
				mr.setOneMeasurement(foodNutrition.getMeasurement());
				unKnownRatio.add(mr);
			} else {
				MeasurementRatio ratio = measurementRatio.get(0);
				if (ratio.getManyMeasurement().equals(
						foodNutrition.getMeasurement())) {
					qty = foodNutrition.getQty().multiply(ratio.getQty());
				} else {
					qty = foodNutrition.getQty().divide(ratio.getQty());
				}

			}
		}
		return qty.multiply(nutritionQty);
	}

	public List<RecipeTime> getRecipeByTime() {
		List<RecipeStep> list = cookDao.getRecipeStepDao().getAll();

		// get time result and consolidate qtys
		Hashtable<String, RecipeTime> recipes = new Hashtable<String, RecipeTime>();
		for (Iterator<RecipeStep> iterator = list.listIterator(); iterator
				.hasNext();) {
			RecipeStep recipeStep = iterator.next();
			int timeTotal = recipeStep.getStep().getTime();
			RecipeTime recipeTime = recipes.remove(recipeStep
					.getRecipe().getName());
			if (recipeTime != null) {
				timeTotal = timeTotal + recipeTime.getTime();
				recipeTime.setTime(timeTotal);
			} else {
				recipeTime = new RecipeTime(recipeStep.getRecipe(),timeTotal); 
			}
			recipes.put(recipeStep.getRecipe().getName(), recipeTime);
		}
		RecipeTime[] consolidatedList = new RecipeTime[recipes.values().size()];
		recipes.values().toArray(consolidatedList);

		// sort by time
		Arrays.sort(consolidatedList, new TimeComparator());
		List<RecipeTime> result = Arrays.asList(consolidatedList);

		return result;
	}

	public List<Cooked> getRecipeByCooked(boolean most) {
		if (most) {
			return cookDao.getCookedDao().findByMostCooked();
		}
		return cookDao.getCookedDao().findByLeastCooked();
	}

	public List<Recipe> getRecipeOfType(String name) {
		return cookDao.getRecipeDao().findByType(name);
	}

	public DairyResult enterFoodDiary(Date whenShop, Date when, Recipe recipe,
			User user) {
		DairyResult dairyResult = new DairyResult();
		if (!MULTI_RECIPE_PER_DAY) {
			List<Diary> clashList = cookDao.getDiaryDao().findByDate(when);
			if (clashList.size() > 0) {
				dairyResult.setDateClash(clashList.get(0));
				return dairyResult;
			}
		}

		long shopToEatLag = when.getTime() - whenShop.getTime();
		shopToEatLag = shopToEatLag / DAY_IN_SEC;

		List list = cookDao.getIngredientDao().findByRecipeName(
				recipe.getName());
		Object[] ingredients = Util.listToArray(list, 0);
		for (int i = 0; i < ingredients.length; i++) {
			Food food = ((Ingredient) ingredients[i]).getFood();
			if (shopToEatLag > food.getUseBy()) {
				dairyResult.setFoodMightGoOff(food);
			}
		}
		if (dairyResult.isOK()) {
			Diary diary = new Diary(when, recipe, user);
			cookDao.getDiaryDao().save(diary);
		}
		return dairyResult;
	}

}
